package com.practice.xml.dom;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.practice.xml.Company;
import com.practice.xml.CompanyDefinition;
import com.practice.xml.Employee;
import com.practice.xml.EmployeeDefinition;
import com.practice.xml.XmlProcessor;

/**
 * Creates and parses XML document by DOM.
 * @author Leonardo
 *
 */
public class DomXmlProcessor implements XmlProcessor {
	
	@Override
	public void createXml(Company company, String fileName){
		Document document = null;
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = factory.newDocumentBuilder();
			document = builder.newDocument();
		} catch (ParserConfigurationException e) {
			System.out.println(e.getMessage());
			System.out.println(e.getStackTrace());
		}
		
		Element companyElement = document.createElement(CompanyDefinition.NAME);
		document.appendChild(companyElement);		
		
		companyElement.setAttribute(CompanyDefinition.ATTRIBUTE_NAME, company.getName());
		
		Element employeesElement = document.createElement(CompanyDefinition.ELEMENT_EMPLOYEES);
		companyElement.appendChild(employeesElement);
		
		for (Employee employee : company.getEmployees()) {
			Element employeeElement = document.createElement(EmployeeDefinition.NAME);
			
			Element nameElement = document.createElement(EmployeeDefinition.ELEMENT_NAME);
			nameElement.appendChild(document.createTextNode(employee.getName()));
			employeeElement.appendChild(nameElement);
			
			Element sexElement = document.createElement(EmployeeDefinition.ELEMENT_SEX);
			sexElement.appendChild(document.createTextNode(Boolean.toString(employee.getSex())));
			employeeElement.appendChild(sexElement);
			
			Element ageElement = document.createElement(EmployeeDefinition.ELEMENT_AGE);
			ageElement.appendChild(document.createTextNode(Integer.toString(employee.getAge())));
			employeeElement.appendChild(ageElement);
			
			employeesElement.appendChild(employeeElement);
		}
		
		TransformerFactory tf = TransformerFactory.newInstance();
		try {
			Transformer transformer = tf.newTransformer();
			DOMSource source = new DOMSource(document);
			transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
			PrintWriter pw = new PrintWriter(new FileOutputStream(fileName));
			StreamResult result = new StreamResult(pw);
			transformer.transform(source, result);
			
			System.out.println("Create XML file completed.");
		} catch (TransformerConfigurationException ex) {
			System.out.println(ex.getMessage());
			System.out.println(ex.getStackTrace());			
		} catch (IllegalArgumentException ex) {
			System.out.println(ex.getMessage());
			System.out.println(ex.getStackTrace());			
		} catch (FileNotFoundException ex) {
			System.out.println(ex.getMessage());
			System.out.println(ex.getStackTrace());
		} catch (TransformerException ex) {
			System.out.println(ex.getMessage());
			System.out.println(ex.getStackTrace());
		}
	}
	
	@Override
	public Company parseXml(String fileName) {
		Document document = null;
		try {
			DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = builderFactory.newDocumentBuilder();
			document = builder.parse(fileName);			
		} catch (FileNotFoundException ex) {
			System.out.println(ex.getMessage());
			System.out.println(ex.getStackTrace());
		} catch (ParserConfigurationException ex) {
			System.out.println(ex.getMessage());
			System.out.println(ex.getStackTrace());
		} catch (SAXException ex) {
			System.out.println(ex.getMessage());
			System.out.println(ex.getStackTrace());
		} catch (IOException ex) {
			System.out.println(ex.getMessage());
			System.out.println(ex.getStackTrace());
		}
		
		Node companyNode = null;
		NodeList documentChildNodes = document.getChildNodes();
		for (int i = 0; i < documentChildNodes.getLength(); i++) {
			Node node = documentChildNodes.item(i);
			if (CompanyDefinition.NAME.equals(node.getNodeName())) {
				companyNode = node;
				break;
			}
		}
		Company company = new Company();
		
		Node nameAttribute = companyNode.getAttributes().getNamedItem(CompanyDefinition.ATTRIBUTE_NAME);
		company.setName(nameAttribute.getTextContent());
		
		NodeList companyChildNodes = companyNode.getChildNodes();
		Node employeesNode = null;
		for (int i = 0; i < companyChildNodes.getLength(); i++) {
			Node node = companyChildNodes.item(i);
			if (CompanyDefinition.ELEMENT_EMPLOYEES.equals(node.getNodeName())) {
				employeesNode = node;
				break;
			}
		}
		List<Employee> employees = new ArrayList<>();
		company.setEmployees(employees);
		
		NodeList employeeNodeList = employeesNode.getChildNodes();
		for (int i = 0; i < employeeNodeList.getLength(); i++) {
			Node employeeNode = employeeNodeList.item(i);
			if (!EmployeeDefinition.NAME.equals(employeeNode.getNodeName())) {
				continue;
			}

			NodeList employeeChildNodes = employeeNode.getChildNodes();
			Employee employee = new Employee();
			for (int j = 0; j < employeeChildNodes.getLength(); j++) {
				Node node = employeeChildNodes.item(j);
				if (EmployeeDefinition.ELEMENT_NAME.equals(node.getNodeName())) {
					employee.setName(node.getTextContent());
				} else if (EmployeeDefinition.ELEMENT_SEX.equals(node.getNodeName())) {
					employee.setSex(Boolean.parseBoolean(node.getTextContent()));
				} else if (EmployeeDefinition.ELEMENT_AGE.equals(node.getNodeName())) {
					employee.setAge(Integer.parseInt(node.getTextContent()));
				}
			}
			employees.add(employee);
		}
		
		System.out.println("Parse XML file completed.");
		return company;
	}
	
}
